--[[
fetch upstream server and save
--]]


ngx.log(ngx.ERR, 'fetcher.lua start')

-- local assert = require('assert') --module 'assert' not found
local myutils = require('myutils')
local request = require('request')
local response = require('response')
-- local sock = require('socket')
local tcp = ngx.socket.tcp
local pairs = pairs
local string = string
local table = table

local function _log(str)
  ngx.log(ngx.ERR, str)
end

local _M = {}
setfenv(1, _M)
local _host = '115.28.0.60' -- '104.236.132.55' -- '127.0.0.1'
local _port = 2181
local _path = '/dubbo'
local _status

function sendByteAndWaitResponse(conn, req, responseType)
  conn:send(req:toStream())
  local bytes = conn:receive(4)
  if not bytes then return nil end
  
  local length = response.unpackInt(bytes)
  -- _log(responseType .. ', length=' .. length)
  local bytes = conn:receive(length)
  -- _log('real length='.. #bytes)
  local resp = response:new(responseType, bytes)
  resp:parse()
  return resp
end


local fetch = function ()
  _log('---------------fetch----------')

  _status = {
    xid = 0,
    lastZxidSeen = 0, -- Uint8Array[8]
    timeOut = 30000,
    sessionId = 0, -- Uint8Array[8]
    passwd = myutils.makeBuffer(16) -- Uint8Array[16]
  }

  -- connect set up

  local sock, err = tcp()
  if not sock then
    _log("tcp() error")
    return
  end
  -- local conn = sock:connect(_host, _port)
  local ok, err = sock:connect(_host, _port)
  if not ok then
    _log("connect() error")
    return
  end
  local conn=sock

  -- send ConnectRequest
  local reqConnect = request:new("ConnectRequest", nil, {
    protocolVersion = 0,
    lastZxidSeen = _status.lastZxidSeen,
    timeOut = _status.timeOut,
    sessionId = _status.sessionId,
    passwd = _status.passwd
  })

  -- handle ConnectResponse
  local respConnect = sendByteAndWaitResponse(conn, reqConnect, "ConnectResponse")
  if not respConnect then return nil end
  _status.timeout = respConnect.payload.timeout
  _status.sessionId = respConnect.payload.sessionId
  _status.passwd = respConnect.payload.passwd
  -- _log('----stage 1 done-----')


  -- send get children request
  -- _log("phase GetChildren2Request")
  _status.xid = _status.xid + 1
  local header = { xid = _status.xid, type = request.OP_CODES.GET_CHILDREN2 }
  local payload = { path = _path, watch = 0 }
  local reqChildren = request:new("GetChildren2Request", header, payload)

  local respChildren = sendByteAndWaitResponse(conn, reqChildren, "getChildren2Response")
    if not respChildren then return nil end
  -- _log('----stage 2 done-----')
  -- for k, v in pairs(respChildren.payload.children) do
  --   _log("child[" .. k .. "]=" .. v)
  -- end

  -- 解析，然后读取 child/provider的children
  local servers = {}
  for _, aPath in pairs(respChildren.payload.children) do

    if string.match(string.lower(aPath), 'rest') then
      local p = '/dubbo/' .. aPath .. '/providers'
      local header = { xid = _status.xid, type = request.OP_CODES.GET_CHILDREN2 }
      local payload = { path = p, watch = 0 }
      local reqChildren = request:new("GetChildren2Request", header, payload)
      local respChildren = sendByteAndWaitResponse(conn, reqChildren, "getChildren2Response")
      if not respChildren then return nil end

      for k, v in pairs(respChildren.payload.children) do
        local restApi = myutils.parseDubboRest(v) -- restApi = { ip_port = ip_port, stype = sType, service = service }
        if restApi then
          local stype = restApi.stype
          if not servers[stype] then servers[stype] = {} end

          -- duplicate remove
          local has = false
          for _, serverip in pairs(servers[stype]) do
            if serverip == restApi.ip_port then has = true break end
          end
          if not has then
            table.insert(servers[stype], restApi.ip_port)
          end

        end
      end
    else
    -- no else
    end
  end

  -- _log('----stage 3 done-----')
  conn:close()
  return servers  -- { type1 : {ip1, ip2}, type2 : {ip1, ip2} }
end


_M.fetch = fetch
return _M













